package watcher

import (
    "fmt"
    "io/ioutil"
    "strings"
    "strconv"
    "regexp"
    "os/user"
    "os"
    "syscall"
    "../pb"
)

var TCPSTATUS = map[string]string{
    "01": "ESTABLISHED",
    "02": "SYN_SENT",
    "03": "SYN_RECV",
    "04": "FIN_WAIT1",
    "05": "FIN_WAIT2",
    "06": "TIME_WAIT",
    "07": "CLOSE",
    "08": "CLOSE_WAIT",
    "09": "LAST_ACK",
    "0A": "LISTEN",
    "0B": "CLOSING",
    "0C": "MAX_STATES",
}

func IsNull(t *pb.Status) bool {
    return t.ProcCount == 0
}
func Formarted(t *pb.Status)[]string{
    //TODO
    return []string{}
}

func Addto(t *pb.Status, o *pb.Status) {
    if o == nil {
        return
    }
    t.CuTime = t.CuTime + o.CuTime
    t.CsTime = t.CsTime + o.CsTime
    t.STime = t.STime + o.STime
    t.BlkioTicks = t.BlkioTicks + o.BlkioTicks
    t.UTime = t.UTime + o.UTime
    t.MemSize = t.MemSize + o.MemSize
    t.ThreadCount = t.ThreadCount + o.ThreadCount
    t.ProcCount = t.ProcCount + o.ProcCount
    if t.TcpCount == nil {
        t.TcpCount = make(map[string]int32)
    }
    for k, v := range o.TcpCount {
        _, ok := t.TcpCount[k]
        if ok {
            t.TcpCount[k] += v
        } else {
            t.TcpCount[k] = v
        }
    }
}

var FormatError = fmt.Errorf("Parse format error")
var HZ = int64(1000)

func ParseStatus(s []byte) (*pb.Status, error) {
    ret := &pb.Status{}
    lines := strings.Split(string(s), " ")
    if len(lines) < 42 {
        return nil, FormatError
    }
    if c, err := strconv.Atoi(lines[19]); err != nil {
        return nil, FormatError
    } else {
        ret.ThreadCount = int32(c)
    }
    var i int64
    var err error
    if i, err = strconv.ParseInt(lines[13], 0, 64); err != nil {
        return nil, FormatError
    }
    ret.UTime = i / HZ
    if i, err = strconv.ParseInt(lines[14], 0, 64); err != nil {
        return nil, FormatError
    }
    ret.STime = i / HZ
    if i, err = strconv.ParseInt(lines[15], 0, 64); err != nil {
        return nil, FormatError
    }
    ret.CuTime = i / HZ
    if i, err = strconv.ParseInt(lines[16], 0, 64); err != nil {
        return nil, FormatError
    }
    ret.CsTime = i / HZ
    if i, err = strconv.ParseInt(lines[22], 0, 64); err != nil {
        return nil, FormatError
    }
    ret.MemSize = i
    if i, err = strconv.ParseInt(lines[41], 0, 64); err != nil {
        return nil, FormatError
    }
    ret.BlkioTicks = i / HZ
    return ret, nil
}

func GetStatus(pid int) (*pb.Status, error) { //user state ctime
    if pid <= 1 {
        return &pb.Status{}, fmt.Errorf("pid is 0 or 1")
    }
    b, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
    if err != nil {
        return nil, err
    }
    s, err := ParseStatus(b)
    s.ProcCount = 1
    if err != nil {
        return nil, err
    }
    
    b, err = ioutil.ReadFile(fmt.Sprintf("/proc/%d/net/tcp", pid))
    if err != nil {
        return nil, err
    }
    s.TcpCount = ParseTcpCount(b)
    return s, nil
}

func ParseTcpCount(b []byte) map[string]int32 {
    lines := strings.Split(string(b), "\n")
    p := regexp.MustCompile("\\s+")
    ret := make(map[string]int32)
    for _, l := range lines {
        fields := p.Split(l, -1)
        if len(fields) < 6 {
            continue
        }
        stat, ok := TCPSTATUS[fields[4]]
        if !ok {
            continue
        }
        ret[stat] = ret[stat] + 1
    }
    return ret
}

func ParseCmdline(pid int) (content []byte, u *user.User, ctime int64, err error) {
    fn := fmt.Sprintf("/proc/%d/cmdline", pid)
    content, err = ioutil.ReadFile(fn)
    if err != nil {
        return
    }
    var fi os.FileInfo
    fi, err = os.Stat(fn)
    if err != nil {
        return
    }
    stat := fi.Sys().(*syscall.Stat_t)
    ctime = int64(stat.Ctim.Sec)
    uid := stat.Uid
    u, err = user.LookupId(fmt.Sprintf("%d", uid))
    return
}
